package com.song.transport;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.song.enumeration.SerializerCode;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;

/**
 * 序列化结果为 二进制 ，而Json本质上是字符串String
 * 在NettyServer和NettyClient类中将 NIO channel的编码方式pipeline.addLast()都换成new KryoSerializer() 即可实现Kryo序列化
 * Kryo专门针对 Java 的序列化方式
 * 推荐使用 Kryo 作为生产环境的序列化方式。(文章地址：https://dubbo.apache.org/zh/docs/v2.7/user/references/protocol/rest/
 * 平均响应时间非常短 平均性能指标较高
 *
 * https://blog.csdn.net/m0_38001814/article/details/103809551
 */
@Slf4j(topic = "c.KryoSerializer")
public class KryoSerializer implements CommonSerializer{
    private static final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
       Kryo kryo = new Kryo();
       kryo.register(RPCRequest.class);//当Kryo在序列化一个对象时，默认需要将类的全限定名称写入，将类名一同写入序列化数据中是比较低效的，所以Kryo支持通过类注册进行优化
       kryo.register(RPCResponse.class);

       kryo.setReferences(true);
       kryo.setRegistrationRequired(false);//setRegistrationRequired 设置为true时，可在遇到任何未注册的类时抛出异常，这能 阻止应用程序使用类名字符串来序列化
       return kryo;
    });


    @Override
    public byte[] serialize(Object obj) {
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
             Output output = new Output(byteArrayOutputStream)){
            Kryo kryo = kryoThreadLocal.get();
            kryo.writeObject(output, obj);
            kryoThreadLocal.remove();
            return output.toBytes();
        } catch (Exception e) {
            log.error("序列化时有错误发生:", e);
//            throw new SerializeException("序列化时有错误发生");
        }
        return null;
    }

    @Override
    public Object deserialize(byte[] bytes, Class<?> clazz) {
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            Input input = new Input(byteArrayInputStream)) {

            Kryo kryo = kryoThreadLocal.get();
            Object obj = kryo.readObject(input, clazz);
            kryoThreadLocal.remove();
            return obj;
        } catch (IOException e) {
            log.error("Kryo反序列化时发生错误:",e);
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public int getCode() {
        return SerializerCode.valueOf("KRYO").getCode();
    }
}
